<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    body {
      background-color: #000;
    }
    canvas {
      background-color: #000;
      border-bottom: 1px solid #fff;
    }
    audio {
      display: block;
      margin: 20px auto;
    }
    .lyrics-box{
      font-family: "Microsoft YaHei";
      font-size: 20px;
      color: #fff;
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
      line-height: 1.5;
    }
  </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <audio src="../image/Zwette、Tom Rosenthal - Go Solo.mp3" controls></audio>
    <div class="lyrics-box">
      <p class="lyrics-em">歌词原译</p>
      <p class="lyrics-zh">歌词大意</p>
    </div>
  <script type="module">
    const canvas = document.querySelector("#canvas");
    const ctx = canvas.getContext("2d");
    const audioEle = document.querySelector("audio");
    const lyricsEM = document.querySelector(".lyrics-em");
    const lyricsZH = document.querySelector(".lyrics-zh");

    function initCanvas() {
      canvas.width = window.innerWidth ;
      canvas.height = window.innerHeight / 2;
    }
    initCanvas();
    let isInit = false;
    let dataArray, analyser;
    let dynColor = getRandomColor();
    const lrcObj = {}; // 歌词对象
    audioEle.onplay = () => {
      if (isInit) return;
      /**
       * 初始化
       * 创建音视频上下文
       * 创建音频源节点
       */
      const audioCtx = new AudioContext();
      const audioSrc = audioCtx.createMediaElementSource(audioEle);
      // 创建分析器
      analyser = audioCtx.createAnalyser();
      // 做变换的窗口大小，默认是2048，值越大分析越细腻，必须是2的n次幂
      analyser.fftSize = 2048;
      // 创建一个数组，用来存储分析器返回的频域数据，analyser.frequencyBinCount = fftSize / 2
      dataArray = new Uint8Array(analyser.frequencyBinCount);
      // 音频源节点和分析器连接
      audioSrc.connect(analyser);
      // 分析器连接到音频设备
      analyser.connect(audioCtx.destination);
      isInit = true;
      setInterval(() => {
        dynColor = getRandomColor();
      }, 2000);
    }

    function draw() {
      requestAnimationFrame(draw);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      if (!isInit) return;

      // 获取频域数据
      analyser.getByteFrequencyData(dataArray);
      // 每个矩形的宽度
      const barWidth = canvas.width / dataArray.length * 2;
      // 低频听不到，取值为一半
      const len = dataArray.length / 2.5;
      ctx.fillStyle = dynColor;
      // 8位整数最大为255，所以取值范围是0-255
      for (let i = 0; i < len; i++) {
        const barHeight = canvas.height * dataArray[i] / 255;
        // 矩形的xy
        // 对称绘制
        const xr = i * barWidth + canvas.width / 2;
        const xl = canvas.width / 2 - i * barWidth;
        const y = canvas.height - barHeight;
        ctx.fillRect(xr, y, barWidth - 1, barHeight);
        ctx.fillRect(xl, y, barWidth - 1, barHeight);
      }
    }
    draw();
    // 随机获取十六进制颜色
    function getRandomColor() {
      const r = Math.floor(Math.random() * 255);
      const g = Math.floor(Math.random() * 255);
      const b = Math.floor(Math.random() * 255);
      return `rgb(${r},${g},${b})`;
    }

    fetch("/web-front/image/Zwette、Tom Rosenthal - Go Solo.lrc").then(res => res.text())
    .then(res=> {
      // console.log(res);
      handleLrc(res);
    });
    // 处理lrc文件
    function handleLrc(lrc) {
      const lrcArr = lrc.split("\n");
      const regex = /\[(\d+:\d+\.\d+)\](.+)/;
      lrcArr.forEach(item => {
        const [ _, timestamp, text ] = item.match(regex);
        const roundTimestamp = timestamp.split('.')[0];
        if (lrcObj[roundTimestamp]) {
          lrcObj[roundTimestamp].zh = text;
        } else {
          lrcObj[roundTimestamp] = {
            em: text
          };
        }
      });
    }

    // 将秒格式化为00:00.000
    function formatTime(currentTime) {
      // 分钟
      const minute = Math.floor(currentTime / 60).toString().padStart(2, 0);
      const second = Math.round(currentTime % 60).toString().padStart(2, 0);
      return `${minute}:${second}`;
    }
    audioEle.ontimeupdate = () => {
      let fmTime = formatTime(audioEle.currentTime);
      writeLrc(fmTime);
    };
    // 写入歌词
    function writeLrc(fmTime) {
      let lrc = lrcObj[fmTime];
      if (lrc) {
        lyricsEM.innerText = lrc.em || "";
        lyricsZH.innerText = lrc.zh || "";
      }
    }
  </script>
</body>
</html>